#include <string.h>

#include "state.h"

static void
past_dispose(void *dat, void *extra)
{
	Branch_act *b_act = dat;
	State *state = extra;

	if (state->act_dispose)
		state->act_dispose(&b_act->act, state->extra);
	else
		free(b_act->act.key);

	free(b_act);
}

Nit_error
state_init(State *state, Acts *acts, uint64_t branch,
	   Act_dispose dispose, void *extra)
{
	Nit_error error;
	Branch_act *root_act;

	state->act_dispose = dispose;
	state->disposer.dispose = past_dispose;
	state->disposer.extra = state;

	if (!(root_act = malloc(sizeof(*root_act))))
		return NIT_ERROR_MEMORY;

	root_act->branch = branch;
	root_act->act.key = NULL;
	root_act->act.real = 1;
	root_act->real_next = NULL;

	if ((error = trie_init(&state->past, &state->disposer,
			       root_act))) {
		free(root_act);
		return error;
	}

	state->end = state->past.root;
	state->acts = acts;
	state->extra = extra;
	state->undoing = 0;
	state->branch = branch;
	return NIT_ERROR_FINE;
}


void
state_dispose(State *state)
{
	trie_dispose(&state->past);
}

Nit_error
state_do(State *state, uint32_t key_len, const void *key,
	 void *dat, void *args, int real)
{
	Trie_entry *tmp;
	Branch_act *b_act = malloc(sizeof(*b_act));
	Nit_error error;

	if (real && !((Branch_act *) state->end->dat)->act.real)
		return NIT_ERROR_ROI;

	if (!b_act)
		return NIT_ERROR_MEMORY;

	if (!(b_act->act.key = malloc(key_len))) {
		free(b_act);
		return NIT_ERROR_MEMORY;
	}

	memcpy(b_act->act.key, key, (b_act->act.key_len = key_len));
	b_act->act.real = real;
	b_act->act.type = ACT_DO;
	b_act->act.dat = dat;
	b_act->act.args = args;
	b_act->branch = state->branch + (state->undoing ? 1 : 0);

	if ((error = acts_act(state->acts, &b_act->act, state->extra))) {
		free(b_act->act.key);
		free(b_act);
		return error;
	}

	if (!(tmp = trie_add(&state->past, state->end, sizeof(state->branch),
			     &state->branch, b_act))) {
		free(b_act->act.key);
		free(b_act);
		return NIT_ERROR_MEMORY;
	}

	if (b_act->act.real)
		((Branch_act *) state->end->dat)->real_next = tmp;

	state->end = tmp;

	if (state->undoing) {
		state->undoing = 0;
		++state->branch;
	}

	return NIT_ERROR_FINE;
}

Nit_error
state_undo(State *state)
{
	Branch_act *b_act = state->end->dat;
	Act act = b_act->act;
	Nit_error error;

	if (!state->end->prev)
		return NIT_ERROR_NOT_UNDOABLE;

	act.type = ACT_UNDO;

	if ((error = acts_act(state->acts, &act, state->extra)))
		return error;

	state->undoing = 1;
	state->end = state->end->prev;
	return NIT_ERROR_FINE;
}

Nit_error
state_redo(State *state, uint64_t branch, int real)
{
	Trie_entry *entry;
	Branch_act *b_act;
	Act act;
	Nit_error error;

	if (real) {
		b_act = state->end->dat;

		if (!b_act->act.real)
			return NIT_ERROR_ROI;

		if (b_act->real_next)
			return NIT_ERROR_REDO_PAST;
	}

	if (!(entry = trie_get(state->end, sizeof(branch), &branch)))
		return NIT_ERROR_NOT_FOUND;

	b_act = entry->dat;
	act = b_act->act;
	act.type = ACT_REDO;
	act.real = 1;

	if ((error = acts_act(state->acts, &act, state->extra)))
		return error;

	b_act->act.real = real;
	state->end = entry;
	return NIT_ERROR_FINE;
}
